package com.ds.lens.data.service;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.ds.lens.data.common.asserts.Asserts;
import com.ds.lens.data.common.constant.Constants;
import com.ds.lens.data.service.dao.LensScheduleDowntimeDao;
import com.ds.lens.data.service.dao.LensScheduleMonitorRelationDao;
import com.ds.lens.data.service.dao.entity.LensMonitor;
import com.ds.lens.data.service.dao.entity.LensScheduleDowntime;
import com.ds.lens.data.service.dao.entity.LensScheduleMonitorRelation;
import com.ds.lens.data.util.DateTimeUtil;
import com.ds.lens.data.vo.in.PagedConditionInVO;
import com.ds.lens.data.vo.in.User;
import com.ds.lens.data.vo.in.v2.downtime.LensRecurringContentInVO;
import com.ds.lens.data.vo.in.v2.downtime.LensScheduleDowntimeInVO;
import com.ds.lens.data.vo.in.v2.downtime.LensScheduleDowntimeUpdateInVO;
import com.ds.lens.data.vo.in.v2.downtime.LensScheduleListInVO;
import com.ds.lens.data.vo.out.PageResult;
import com.ds.lens.data.vo.out.v2.monitor.LensMonitorOutVO;
import com.ds.lens.data.vo.out.v2.schedule.LensRecurringContentOutVO;
import com.ds.lens.data.vo.out.v2.schedule.LensScheduleListOutVO;
import com.ds.lens.data.vo.out.v2.schedule.LensScheduleOutVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * Description:
 *
 * @author hill
 * @date 2019-11-06
 */
@Slf4j
@Service
public class LensScheduleDowntimeService extends BaseService {

    @Autowired
    LensScheduleDowntimeDao lensScheduleDowntimeDao;

    @Autowired
    LensScheduleMonitorRelationDao lensScheduleMonitorRelationDao;

    @Autowired
    LensMonitorMuteService lensMonitorMuteService;

    @Autowired
    LensMonitorService lensMonitorService;

    @Transactional
    public Boolean saveDowntime(LensScheduleDowntimeInVO lensScheduleDowntimeInVO, User user) {
        checkUserApplication(user, lensScheduleDowntimeInVO.getApplicationId());
        Asserts.assertFalse(StringUtils.isEmpty(lensScheduleDowntimeInVO.getMonitorId()) &&
                CollectionUtils.isEmpty(lensScheduleDowntimeInVO.getMonitorTags()), Constants.ErrorCode.SCHEDULE_PARAM_EXIST);
        LensScheduleDowntime lensScheduleDowntime = new LensScheduleDowntime();
        BeanUtils.copyProperties(lensScheduleDowntimeInVO, lensScheduleDowntime);
        lensScheduleDowntime.setId(null);
        lensScheduleDowntime.setCreatedTime(new Date());
        lensScheduleDowntime.setCreator(user.getUsername());
        lensScheduleDowntime.setMender(lensScheduleDowntime.getCreator());
        lensScheduleDowntime.setModifiedTime(lensScheduleDowntime.getCreatedTime());
        if (!CollectionUtils.isEmpty(lensScheduleDowntimeInVO.getMonitorTags())) {
            lensScheduleDowntime.setMonitorTags(String.join(",", lensScheduleDowntimeInVO.getMonitorTags()));
        } else {
            lensScheduleDowntime.setMonitorTags("");
        }
        if (lensScheduleDowntimeInVO.getScheduleType() == 2) {
            setRecurring(lensScheduleDowntime, lensScheduleDowntimeInVO.getRecurringContent(), lensScheduleDowntimeInVO.getStartTime());
        }
        lensScheduleDowntimeDao.insertSelective(lensScheduleDowntime);
        saveMonitorRelation(lensScheduleDowntime);
        return true;
    }

    @Transactional
    public Boolean updateDowntime(LensScheduleDowntimeUpdateInVO lensScheduleDowntimeInVO, User user) {
        checkUserApplication(user, lensScheduleDowntimeInVO.getApplicationId());
        Asserts.assertFalse(StringUtils.isEmpty(lensScheduleDowntimeInVO.getMonitorId()) &&
                CollectionUtils.isEmpty(lensScheduleDowntimeInVO.getMonitorTags()), Constants.ErrorCode.SCHEDULE_PARAM_EXIST);
        LensScheduleDowntime lensScheduleDowntime = new LensScheduleDowntime();
        BeanUtils.copyProperties(lensScheduleDowntimeInVO, lensScheduleDowntime);
        lensScheduleDowntime.setMender(user.getUsername());
        lensScheduleDowntime.setModifiedTime(new Date());
        if (!CollectionUtils.isEmpty(lensScheduleDowntimeInVO.getMonitorTags())) {
            lensScheduleDowntime.setMonitorTags(String.join(",", lensScheduleDowntimeInVO.getMonitorTags()));
        } else {
            lensScheduleDowntime.setMonitorTags("");
        }
        if (lensScheduleDowntimeInVO.getScheduleType() == 2) {
            setRecurring(lensScheduleDowntime, lensScheduleDowntimeInVO.getRecurringContent(), lensScheduleDowntimeInVO.getStartTime());
        }
        int flag = lensScheduleDowntimeDao.updateByPrimaryKeySelective(lensScheduleDowntime);
        saveMonitorRelation(lensScheduleDowntime);
        Asserts.assertTrue(flag == 1, Constants.ErrorCode.DATA_NOT_EXIST);
        return true;
    }

    private void saveMonitorRelation(LensScheduleDowntime lensScheduleDowntime) {
        if (lensScheduleDowntime.getMonitorId() != null) {
            List<LensScheduleMonitorRelation> records = new ArrayList<>(1);
            LensScheduleMonitorRelation record = new LensScheduleMonitorRelation();
            record.setMonitorId(lensScheduleDowntime.getMonitorId());
            record.setScheduleId(lensScheduleDowntime.getId());
            records.add(record);
            lensScheduleMonitorRelationDao.deleteByScheduleId(lensScheduleDowntime.getId());
            lensScheduleMonitorRelationDao.batchInsert(records);
        }
    }

    private void setRecurring(LensScheduleDowntime lensScheduleDowntime, LensRecurringContentInVO recurring, Date startTime) {
        Asserts.assertTrue(recurring.getRepeatType().getValue() <= recurring.getDurationType().getValue(),
                Constants.ErrorCode.RECURRING_DATE_EXCEPTION);
        Asserts.assertFalse(recurring.getRepeatType() == recurring.getDurationType() &&
                recurring.getRepeat() <= recurring.getDuration(), Constants.ErrorCode.RECURRING_DATE_EXCEPTION);
        lensScheduleDowntime.setEndTime(DateTimeUtil
                .dateAdd(recurring.getDurationType().getValue(), startTime, recurring.getDuration()));
        lensScheduleDowntime.setRecurringContent(JSON.toJSONString(recurring));
    }

    @Transactional
    public Boolean deleteDowntimeById(Long id, User user) {
        LensScheduleDowntime lensScheduleDowntime = lensScheduleDowntimeDao.selectByPrimaryKey(id);
        Asserts.assertTrue(lensScheduleDowntime != null, Constants.ErrorCode.DATA_NOT_EXIST);
        checkUserApplication(user, lensScheduleDowntime.getApplicationId());
        lensScheduleDowntimeDao.deleteByPrimaryKey(id, user.getUsername(), new Date());
        if (lensScheduleDowntime.getState() == 2) {
            if (lensScheduleDowntime.getMonitorId() != null) {
                checkMonitorMute(lensScheduleDowntime.getMonitorId(), lensScheduleDowntime.getEndTime(), user);
            } else if (!StringUtils.isEmpty(lensScheduleDowntime.getMonitorTags())) { //根据标签查询
                List<LensScheduleMonitorRelation> relationList = lensScheduleMonitorRelationDao
                        .selectByScheduleId(lensScheduleDowntime.getId());
                relationList.forEach(lensScheduleMonitorRelation -> {
                    checkMonitorMute(lensScheduleMonitorRelation.getMonitorId(), lensScheduleDowntime.getEndTime(), user);
                });
            }
        }
        return true;
    }

    private void checkMonitorMute(Long monitorId, Date scheduleEndTime, User user) {
        List<LensScheduleMonitorRelation> relationList = lensScheduleMonitorRelationDao
                .selectByMonitorId(Collections.singletonList(monitorId));
        LensMonitor lensMonitor = lensMonitorDao.selectByPrimaryKey(monitorId);
        if (lensMonitor == null) {
            return;
        } else if (lensMonitor.getMuteEndTime() == null && scheduleEndTime != null) {
            return;
        } else if (lensMonitor.getMuteEndTime() != null && scheduleEndTime != null
                && lensMonitor.getMuteEndTime().getTime() > scheduleEndTime.getTime()) {
            return;
        }
        if (CollectionUtils.isNotEmpty(relationList)) {
            Long maxDateTime = null;
            for (LensScheduleMonitorRelation lensScheduleMonitorRelation : relationList) {
                if (lensScheduleMonitorRelation.getEndTime() == null) {
                    maxDateTime = null;
                    break;
                } else if (maxDateTime == null) {
                    maxDateTime = lensScheduleMonitorRelation.getEndTime().getTime();
                } else if (maxDateTime < lensScheduleMonitorRelation.getEndTime().getTime()) {
                    maxDateTime = lensScheduleMonitorRelation.getEndTime().getTime();
                }
            }
            Date endTime = null;
            if (maxDateTime != null) {
                endTime = new Date(maxDateTime);
            }
            lensMonitorMuteService.scheduleMuteMonitor(lensMonitor, user.getUsername(), true,
                    lensMonitor.getMuteStartTime(), endTime, true);
        } else {
            lensMonitorMuteService.scheduleMuteMonitor(lensMonitor, user.getUsername(), false,
                    null, null, true);
        }
    }


    public LensScheduleOutVO getDownTime(Long id, User user) {
        LensScheduleDowntime lensScheduleDowntime = lensScheduleDowntimeDao.selectByPrimaryKey(id);
        Asserts.assertTrue(lensScheduleDowntime != null, Constants.ErrorCode.DATA_NOT_EXIST);
        checkUserApplication(user, lensScheduleDowntime.getApplicationId());
        LensScheduleOutVO lensScheduleOutVO = new LensScheduleOutVO();
        BeanUtils.copyProperties(lensScheduleDowntime, lensScheduleOutVO);
        lensScheduleOutVO.setStartTime(lensScheduleDowntime.getStartTime().getTime());
        lensScheduleOutVO.setCreatedTime(lensScheduleDowntime.getCreatedTime().getTime());
        lensScheduleOutVO.setModifiedTime(lensScheduleDowntime.getModifiedTime().getTime());
        if (lensScheduleDowntime.getEndTime() != null) {
            lensScheduleOutVO.setEndTime(lensScheduleDowntime.getEndTime().getTime());
        }
        if (!StringUtils.isEmpty(lensScheduleDowntime.getMonitorTags())) {
            lensScheduleOutVO.setMonitorTags(lensScheduleDowntime.getMonitorTags().split(","));
        }
        if (!StringUtils.isEmpty(lensScheduleDowntime.getRecurringContent())) {
            LensRecurringContentInVO recurring = JSON.parseObject(lensScheduleDowntime.getRecurringContent(), LensRecurringContentInVO.class);
            LensRecurringContentOutVO recurringOut = new LensRecurringContentOutVO();
            BeanUtils.copyProperties(recurring, recurringOut);
            if (recurring.getEndTime() != null) {
                recurringOut.setEndTime(recurring.getEndTime().getTime());
            }
            lensScheduleOutVO.setRecurringContent(recurringOut);
        }
        return lensScheduleOutVO;
    }

    @Transactional
    public void deleteDowntimeByMonitorId(Long monitorId, String userName) {
        lensScheduleDowntimeDao.deleteByPrimaryMonitorId(monitorId, userName, new Date());
    }

    @Transactional
    public void deleteAllDowntimeByMonitorId(Long monitorId, String userName) {
        lensScheduleDowntimeDao.deleteAllByPrimaryMonitorId(monitorId, userName, new Date());
    }


    @Transactional
    public void updateMonitorName(Long monitorId, String monitorName) {
        lensScheduleDowntimeDao.updateMonitorNameByMonitorId(monitorId, monitorName);
    }

    public PageResult<LensScheduleListOutVO> listDowntime(PagedConditionInVO<LensScheduleListInVO> conditionInVO, User user) {
        getPageCondition(conditionInVO);
        if (CollectionUtils.isEmpty(conditionInVO.getCondition().getApplicationIds())) {
            checkUserApplication(user);
            conditionInVO.getCondition().setApplicationIds(user.getApplicationIds());
        } else {
            checkUserApplication(user, conditionInVO.getCondition().getApplicationIds());
        }
        Page<LensScheduleDowntime> page = lensScheduleDowntimeDao.queryDowntime(conditionInVO.getCondition());
        if (CollectionUtils.isEmpty(page.getResult())) {
            return PageResult.of(Collections.emptyList(), 0L, page.getPageNum(), page.getPageSize());
        }
        List<LensScheduleListOutVO> result = new ArrayList<>(page.getPageSize());
        page.getResult().forEach(lensScheduleDowntime -> {
            LensScheduleListOutVO lensScheduleListOutVO = new LensScheduleListOutVO();
            BeanUtils.copyProperties(lensScheduleDowntime, lensScheduleListOutVO);
            lensScheduleListOutVO.setStartTime(lensScheduleDowntime.getStartTime().getTime());
            if (lensScheduleDowntime.getEndTime() != null) {
                lensScheduleListOutVO.setEndTime(lensScheduleDowntime.getEndTime().getTime());
            }
            if (!StringUtils.isEmpty(lensScheduleDowntime.getMonitorTags())) {
                lensScheduleListOutVO.setMonitorTags(lensScheduleDowntime.getMonitorTags().split(","));
            }
            result.add(lensScheduleListOutVO);
        });
        return PageResult.of(result, page.getTotal(), page.getPageNum(), page.getPageSize());
    }

    public List<LensMonitorOutVO> monitorList(Long id, User user) {
        LensScheduleDowntime lensScheduleDowntime = lensScheduleDowntimeDao.selectByPrimaryKey(id);
        checkUserApplication(user, lensScheduleDowntime.getApplicationId());
        if (lensScheduleDowntime.getMonitorId() != null) {
            LensMonitor lensMonitor = lensMonitorDao.selectByPrimaryKey(lensScheduleDowntime.getMonitorId());
            if (lensMonitor != null) {
                return encapsulationMonitorVO(Collections.singletonList(lensMonitor));
            }
        } else {
            if (lensScheduleDowntime.getState() == 2) {
                List<LensScheduleMonitorRelation> relationList = lensScheduleMonitorRelationDao
                        .selectByScheduleId(lensScheduleDowntime.getId());
                if (CollectionUtils.isNotEmpty(relationList)) {
                    List<Long> ids = new ArrayList<>();
                    relationList.forEach(relation -> {
                        ids.add(relation.getMonitorId());
                    });
                    List<LensMonitor> lensMonitors = lensMonitorDao.selectByPrimaryKeys(ids);
                    if (CollectionUtils.isNotEmpty(lensMonitors)) {
                        return encapsulationMonitorVO(lensMonitors);
                    }
                }
            } else {
                List<LensMonitor> lensMonitors = lensMonitorMuteService.getMonitorByTags(
                        lensScheduleDowntime.getApplicationId(), Arrays.asList(lensScheduleDowntime.getMonitorTags().split(",")));
                if (CollectionUtils.isNotEmpty(lensMonitors)) {
                    return encapsulationMonitorVO(lensMonitors);
                }
            }
        }
        return null;
    }

    private List<LensMonitorOutVO> encapsulationMonitorVO(List<LensMonitor> lensMonitors) {
        List<LensMonitorOutVO> lensMonitorOutVOS = new ArrayList<>(lensMonitors.size());
        lensMonitors.forEach(lensMonitor -> {
            LensMonitorOutVO outVO = new LensMonitorOutVO();
            BeanUtils.copyProperties(lensMonitor, outVO);
            if (!StringUtils.isEmpty(lensMonitor.getTags())) {
                outVO.setTags(lensMonitor.getTags().split(","));
            }
            lensMonitorOutVOS.add(outVO);
        });
        return lensMonitorOutVOS;
    }

    public List<LensScheduleDowntime> listAllSchedule() {
        return lensScheduleDowntimeDao.listAllSchedule();
    }

    @Transactional
    public void scheduleMute(Long id, Boolean mute) {
        LensScheduleDowntime lensScheduleDowntime = lensScheduleDowntimeDao.selectByPrimaryKey(id);
        //计划不存在退出
        if (lensScheduleDowntime == null || lensScheduleDowntime.getIsDelete()) {
            return;
        }
        //需要静默，但是计划已被执行退出
        if (mute && lensScheduleDowntime.getState() == 2) {
            return;
        }
        if (mute) {
            lensScheduleDowntimeDao.updateStateById(id, 2);
        } else if (lensScheduleDowntime.getScheduleType() == 2 && lensScheduleDowntime.getState() == 1) {
            lensScheduleDowntimeDao.updateStateById(id, 1);
        }
        //根据告警ID建的计划
        if (lensScheduleDowntime.getMonitorId() != null) {
            LensMonitor lensMonitor = lensMonitorMuteService.getMonitorById(lensScheduleDowntime.getMonitorId());
            processMonitor(lensMonitor, lensScheduleDowntime, mute);
        } else if (!StringUtils.isEmpty(lensScheduleDowntime.getMonitorTags())) { //根据标签查询
            if (mute) {
                List<LensMonitor> lensMonitors = lensMonitorMuteService.getMonitorByTags(
                        lensScheduleDowntime.getApplicationId(), Arrays.asList(lensScheduleDowntime.getMonitorTags().split(",")));
                List<LensScheduleMonitorRelation> records = new ArrayList<>(lensMonitors.size());
                lensMonitors.forEach(lensMonitor -> {
                    LensScheduleMonitorRelation record = new LensScheduleMonitorRelation();
                    record.setMonitorId(lensMonitor.getId());
                    record.setScheduleId(lensScheduleDowntime.getId());
                    records.add(record);
                    processMonitor(lensMonitor, lensScheduleDowntime, mute);
                });
                if (CollectionUtils.isNotEmpty(records)) {
                    lensScheduleMonitorRelationDao.deleteByScheduleId(lensScheduleDowntime.getId());
                    lensScheduleMonitorRelationDao.batchInsert(records);
                }
            } else {
                List<LensScheduleMonitorRelation> relationList = lensScheduleMonitorRelationDao
                        .selectByScheduleId(lensScheduleDowntime.getId());
                if (CollectionUtils.isNotEmpty(relationList)) {
                    List<Long> ids = new ArrayList<>(relationList.size());
                    relationList.forEach(lensScheduleMonitorRelation -> {
                        ids.add(lensScheduleMonitorRelation.getMonitorId());
                    });
                    List<LensMonitor> lensMonitors = lensMonitorDao.selectByPrimaryKeys(ids);
                    lensMonitors.forEach(lensMonitor -> {
                        processMonitor(lensMonitor, lensScheduleDowntime, mute);
                    });
                }
            }

        }
    }

    private void processMonitor(LensMonitor lensMonitor, LensScheduleDowntime lensScheduleDowntime, Boolean mute) {
        if (lensMonitor == null) {
            return;
        }
        if (lensScheduleDowntime.getScheduleType() == 1) {
            if (!mute && (lensMonitor.getMuteEndTime() == null ||
                    lensMonitor.getMuteEndTime().getTime() > lensScheduleDowntime.getEndTime().getTime())) {
                lensScheduleDowntimeDao.deleteByPrimaryKey(lensScheduleDowntime.getId(), lensScheduleDowntime.getMender(), new Date());
                return;
            }
        } else {
            if (!mute) {
                LensRecurringContentInVO recurring = JSON.parseObject(lensScheduleDowntime.getRecurringContent(), LensRecurringContentInVO.class);
                if (recurring.getEndTime() == null || recurring.getEndTime().getTime() > lensScheduleDowntime.getEndTime().getTime()) {
                    Date startTime = DateTimeUtil.dateAdd(
                            recurring.getRepeatType().getValue(), lensScheduleDowntime.getStartTime(), recurring.getRepeat());
                    Date endTime = DateTimeUtil.dateAdd(recurring.getDurationType().getValue(), startTime, recurring.getDuration());
                    lensScheduleDowntimeDao.updateRecurringDateById(lensScheduleDowntime.getId(), 1, startTime, endTime);
                } else {
                    lensScheduleDowntimeDao.deleteByPrimaryKey(lensScheduleDowntime.getId(), lensScheduleDowntime.getMender(), new Date());
                }
                if (lensMonitor.getMuteEndTime() == null || lensMonitor.getMuteEndTime().getTime() > lensScheduleDowntime.getEndTime().getTime()) {
                    return;
                }
            }
        }
        lensMonitorMuteService.scheduleMuteMonitor(lensMonitor, lensScheduleDowntime.getMender(),
                mute, lensScheduleDowntime.getStartTime(), lensScheduleDowntime.getEndTime(), false);
    }
}
